﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TextManager.Interop;

namespace PovTeam.Pkg_PovrayProject
{

    class PovCodeContext
    {
        IVsTextLines buffer;

        private static readonly IdentifierType[] FloatOrFunction = new IdentifierType[] { IdentifierType.Float, IdentifierType.Function };
        private static readonly IdentifierType[] Float = new IdentifierType[] { IdentifierType.Float};
        private static readonly IdentifierType[] BoolOrFunction = new IdentifierType[] { IdentifierType.Boolean, IdentifierType.Function };
        private static readonly IdentifierType[] StringOrFunction = new IdentifierType[] { IdentifierType.String, IdentifierType.Function };
        private static readonly IdentifierType[] VectorOrFunction = new IdentifierType[] { IdentifierType.Vector, IdentifierType.Float, IdentifierType.Function };
        //private static readonly IdentifierType[] ObjectModifiers = new IdentifierType[] { IdentifierType.Transform, IdentifierType.Texture, IdentifierType.Pigment, IdentifierType.Finish, IdentifierType.Normal, IdentifierType.Interior, IdentifierType.Media, IdentifierType.Material };
        private static readonly IdentifierType[] Unknown = new IdentifierType[] { IdentifierType.Unknown };
        private static readonly IdentifierType[] Any = Unknown;
        private static readonly IdentifierType[] Bool = new IdentifierType[] { IdentifierType.Boolean};
        private static readonly IdentifierType[] String= new IdentifierType[] { IdentifierType.String};
        private static readonly IdentifierType[] Vector= new IdentifierType[] { IdentifierType.Vector};


        public PovCodeContext(IVsTextLines buffer)
        {
            this.buffer = buffer;
        }
        private static IdentifierType[] IdentList(params IdentifierType[] list)
        {
            return list;
        }
        private string GetLine(int line)
        {
            string sLine;
            int lineLen;

            buffer.GetLengthOfLine(line, out lineLen);
            buffer.GetLineText(line, 0, line, lineLen, out sLine);

            return sLine;
        }

        private string FindPrevToken(string text, ref int pos)
        {
            // First, find a non white-space character. 
            while (pos > 0 && char.IsWhiteSpace(text[pos]))
            {
                pos--;
            }

            if (pos == 0)
            {
                pos = -1;
                return string.Empty;
            }

            int end = pos + 1;

            // Next, Gather digits and letters in groups, operators
            // will return individually
            bool prevLetter = false;
            while (pos >= 0)
            {
                if (char.IsLetterOrDigit(text[pos]) || text[pos] == '_')
                {
                    prevLetter = true;
                }
                else if (char.IsSymbol(text[pos]) || char.IsPunctuation(text[pos]))
                {
                    int loc = prevLetter ? (pos + 1) : pos;
                    string tok = text.Substring(loc, end - loc);
                    if (prevLetter) return tok;
                    pos--;
                    return tok;
                }
                else if (char.IsWhiteSpace(text[pos]))
                {
                    int loc = pos + 1;
                    return text.Substring(loc, end - loc);
                }
                pos--;
            }
            return string.Empty;
        }

        public CompletionItem[] ValidKeywords(int line, int index)
        {
            string sText;

            int lastLineLen;
            int last;

            buffer.GetLastLineIndex(out last, out lastLineLen);
            buffer.GetLengthOfLine(last, out lastLineLen);
            buffer.GetLineText(0, 0, last, lastLineLen, out sText);

            int pos;

            buffer.GetPositionOfLineIndex(line, index, out pos);

            // Strip out all of the comments
            sText = StripComments(sText, ref pos);

            bool found = false;
            int argNum = 0;
            string token = "";
            // Walk back through the tokens, looking for a keyword. 
            // Argument count is tracked so keywords that have multiple
            // arguments expecting different types can be handles appropriately. 
            while (!found)
            {
                token = FindPrevToken(sText, ref pos);

                if (PovCodeCompletion.keywords.Contains(token))
                {
                    found = true;
                }
                else if (token == ",")
                {
                    argNum++;
                }

            }

            return new CompletionItem[0];
        }

        public IdentifierType[] MinimalValidIdentifierTypes(int line, int index)
        {

            string sText;

            int lastLineLen;
            int last;

            buffer.GetLastLineIndex(out last, out lastLineLen);
            buffer.GetLengthOfLine(last, out lastLineLen);
            buffer.GetLineText(0, 0, last, lastLineLen, out sText);

            int pos;

            buffer.GetPositionOfLineIndex(line, index, out pos);

            // Strip out all of the comments
            sText = StripComments(sText, ref pos);

            bool found = false;
            int argNum = 0;
            string token = "";
            // Walk back through the tokens, looking for a keyword. 
            // Argument count is tracked so keywords that have multiple
            // arguments expecting different types can be handles appropriately. 
            while (!found)
            {
                token = FindPrevToken(sText, ref pos);

                if (PovCodeCompletion.keywords.Contains(token))
                {
                    found = true;
                }
                else if (token == ",")
                {
                    argNum++;
                }

            }


            if (token == "agate")
            {
                if (IsInNormalBlock(sText, ref pos, ref token))
                {
                    return IdentList(IdentifierType.Float);
                }
                else
                {
                    return MapType(sText, ref pos, ref token);
                }

            }

            if (token == "average") return MapType(sText, ref pos, ref token);

            if (token == "aa_level") return Float;
            if (token == "aa_threshold") return Float;
            if (token == "abs") return Float;
            if (token == "absorption") return ColorOnly;
            if (token == "accuracy") return Float;
            if (token == "acos") return Float;
            if (token == "acosh") return Float;
            if (token == "adc_bailout") return Float;
            if (token == "agate_turb") return Float;
            if (token == "albedo") return Float;
            if (token == "all") return Float;
            if (token == "alpha") return Float;
            if (token == "altitude") return Float;
            if (token == "always_sample") return Bool;
            if (token == "angle") return Float;
            if (token == "aoi") return Float;
            if (token == "aperture") return Float;
            if (token == "asin") return Float;
            if (token == "asinh") return Float;
            if (token == "assumed_gamma") return Float;
            if (token == "atan") return Float;
            if (token == "atan2") return Float;
            if (token == "atand") return Float;
            if (token == "atanh") return Float;

            if (token == "ambient") return ColorOnly;
            if (token == "ambient_light") return ColorOnly;
            if (token == "arc_angle") return Float;
            if (token == "background") return ColorOnly;

            if (token == "area_illumination") return Bool;

            if (token == "asc") return String;

            if (token == "black_hole") return Vector;

            if (token == "bokeh") return new IdentifierType[] { IdentifierType.Pigment };


            if (token == "b_spline") return Float;


            if (token == "autostop") return Float;
            if (token == "bezier_spline")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "bitwise_and") return Float;
            if (token == "bitwise_or") return Float;
            if (token == "bitwise_xor") return Float;
            if (token == "blue") return Float;
            if (token == "blur_samples") return Float;

            if (token == "bounded_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "box") return Vector;
            if (token == "boxed") return MapType(sText, ref pos, ref token);
            if (token == "bozo") return MapType(sText, ref pos, ref token);
            if (token == "brick") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "brick_size") return Float;
            if (token == "brightness") return Float;
            if (token == "brilliance") return Float;

            if (token == "bump_size") return Float;
            if (token == "bumps") return MapType(sText, ref pos, ref token);
            if (token == "camera") return new IdentifierType[] { IdentifierType.Camera };
            if (token == "case") return Float;
            if (token == "caustics") return Float;
            if (token == "ceil") return Float;
            if (token == "cells") return MapType(sText, ref pos, ref token);

            if (token == "checker") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "chr") return Float;
            if (token == "clipped_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "collect") return Bool;
            if (token == "color") return ColorOnly;
            if (token == "color_map") return new IdentifierType[] { IdentifierType.ColorMap };
            if (token == "colour") return ColorOnly;
            if (token == "colour_map") return new IdentifierType[] { IdentifierType.ColorMap };
            if (token == "component")
            {
                if (argNum == 2) return Vector;
                return Float;
            }
            if (token == "composite") return new IdentifierType[] { IdentifierType.Object };
            if (token == "concat") return new IdentifierType[] { IdentifierType.String };
            if (token == "cone")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Float;
                if (argNum == 2) return Vector;
                if (argNum == 3) return Float;
               
            }
            if (token == "confidence") return Float;
            if (token == "conic_sweep")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "contained_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "control0") return Float;
            if (token == "control1") return Float;
            if (token == "coords") return Vector;
            if (token == "cos") return Float;
            if (token == "cosh") return Float;
            if (token == "count") return Float;
            if (token == "crackle") return MapType(sText, ref pos, ref token);
            if (token == "crand") return Float;
            if (token == "cubic") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "cubic_spline")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "cubic_wave") return MapType(sText, ref pos, ref token);
            if (token == "cylinder")
            {
                if (argNum == 0 || argNum == 1) return Vector;
                if (argNum == 2) return Float;
                
            }
            if (token == "cylindrical") return MapType(sText, ref pos, ref token);
            if (token == "datetime")
            {
                if (argNum == 0) return Float;
                if (argNum == 1) return new IdentifierType[] { IdentifierType.String };
            }
            if (token == "debug") return new IdentifierType[] { IdentifierType.String };

            if (token == "default") return IdentList(IdentifierType.Texture, IdentifierType.Pigment, IdentifierType.Finish, IdentifierType.Normal);
            if (token == "degrees") return Float;
            if (token == "density") return IdentList(IdentifierType.DensityMap);
            if (token == "density_file") return IdentList(IdentifierType.String);
            if (token == "density_map") return IdentList(IdentifierType.DensityMap);
            if (token == "dents") return MapType(sText, ref pos, ref token);
            if (token == "df3") return IdentList(IdentifierType.String);
            if (token == "difference") IdentList(IdentifierType.Object);
            if (token == "diffuse") return Vector;
            if (token == "dimension_size")
            {
                if (argNum == 0) return IdentList(IdentifierType.Array);
                return IdentList(IdentifierType.Float);
            }
            if (token == "dimensions") return IdentList(IdentifierType.Array);
            if (token == "direction") return Vector;
            if (token == "disc")
            {
                if (argNum == 0 || argNum == 1) return Vector;
                if (argNum == 2) return Float;
                if (argNum == 3) return Float;
                
            }
            if (token == "dispersion") return Float;
            if (token == "dispersion_samples") return Float;
            if (token == "dist_exp") return Float;
            if (token == "distance") return Float;
            if (token == "div") return Float;
            if (token == "eccentricity") return Float;
            if (token == "emission") return ColorOnly;
            if (token == "error") return IdentList(IdentifierType.String);
            if (token == "error_bound") return Float;
            if (token == "evaluate") return Float;
            if (token == "exp") return Float;
            if (token == "expand_thresholds") return Float;
            if (token == "exponent") return Float;
            if (token == "extinction") return Float;
            if (token == "face_indices")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "facets") return MapType(sText, ref pos, ref token);
            if (token == "fade_color") return ColorOnly;
            if (token == "fade_colour") return ColorOnly;
            if (token == "fade_distance") return Float;
            if (token == "fade_power") return Float;
            if (token == "falloff") return Float;
            if (token == "falloff_angle") return Float;
            if (token == "fclose") return IdentList(IdentifierType.FileIdentifier);
            if (token == "file_exists") return IdentList(IdentifierType.String);
            if (token == "filter") return Float;
            if (token == "finish") return IdentList(IdentifierType.Finish);
            if (token == "flatness") return Float;
            if (token == "flip") return Vector;
            if (token == "floor") return Float;
            if (token == "focal_point") return Vector;
            if (token == "fog") return IdentList(IdentifierType.Fog);
            if (token == "fog_alt") return Float;
            if (token == "fog_offset") return Float;
            if (token == "fog_type") return Float;
            if (token == "for") return Float;
            if (token == "form") return Vector;
            if (token == "frequency") return Float;
            if (token == "fresnel") return Bool;
            if (token == "gamma") return Float;
            if (token == "gather") return Float;
            if (token == "gif") return IdentList(IdentifierType.String);
            if (token == "global_lights") return Bool;
            if (token == "gradient") return MapType(sText, ref pos, ref token);
            if (token == "granite") return MapType(sText, ref pos, ref token);
            if (token == "gray_threshold") return Float;
            if (token == "green") return Float;
            if (token == "hexagon") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "hierarchy") return Bool;
            if (token == "hollow") return Bool;
            if (token == "if") return Bool;
            if (token == "iff") return IdentList(IdentifierType.String);
            if (token == "importance") return Float;
            if (token == "include") return IdentList(IdentifierType.String);
            if (token == "inside")
            {
                if (argNum == 0) return IdentList(IdentifierType.Object);
                return Vector;
            }
            if (token == "inside_vector") return Vector;
            if (token == "int") return Float;
            if (token == "interior") return IdentList(IdentifierType.Interior);
            if (token == "interior_texture") return IdentList(IdentifierType.Texture, IdentifierType.Pigment, IdentifierType.Normal, IdentifierType.Finish);
            if (token == "internal") return Float;
            if (token == "interpolate") return Float;
            if (token == "intersection") return IdentList(IdentifierType.Object);
            if (token == "intervals") return Float;
            if (token == "ior") return MapType(sText, ref pos, ref token);
            if (token == "irid") return Float;
            if (token == "irid_wavelength") return Float;
            if (token == "isosurface") return IdentList(IdentifierType.Function);
            if (token == "jitter") return Float;
            if (token == "jpeg") return IdentList(IdentifierType.String);
            if (token == "julia")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Float;
                if (argNum == 2) return Float;
            }
            if (token == "julia_fractal") return Vector;
            if (token == "lambda") return Float;
            if (token == "leopard") return MapType(sText, ref pos, ref token);
            if (token == "light_group") return IdentList(IdentifierType.Object, IdentifierType.LightSource);
            if (token == "light_source") return IdentList(IdentifierType.LightSource, IdentifierType.Vector);
            if (token == "linear_spline")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "linear_sweep")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "ln") return Float;
            if (token == "load_file") return IdentList(IdentifierType.String);
            if (token == "location") return Vector;
            if (token == "log") return Float;
            if (token == "look_at") return Vector;
            if (token == "looks_like") return IdentList(IdentifierType.Object);
            if (token == "low_error_factor") return Float;
            if (token == "magnet")
            {
                if (argNum == 0) return Float;
                else return new IdentifierType[] { };
            }
            if (token == "major_radius") return Float;
            if (token == "mandel") return Float;
            if (token == "map_type") return Float;
            if (token == "marble") return MapType(sText, ref pos, ref token);
            if (token == "material") return IdentList(IdentifierType.Material);
            if (token == "max") return Float;
            if (token == "maximum_reuse") return Float;
            if (token == "max_extent") return IdentList(IdentifierType.Object);
            if (token == "max_gradient") return Float;
            if (token == "max_intersections") return Float;
            if (token == "max_iteration") return Float;
            if (token == "max_sample") return Float;
            if (token == "max_trace") return Float;
            if (token == "max_trace_level") return Float;
            if (token == "media") return IdentList(IdentifierType.Media);
            if (token == "media_attenuation") return Bool;
            if (token == "media_interaction") return Bool;
            if (token == "merge") return IdentList(IdentifierType.Object);
            if (token == "method") return Float;
            if (token == "metric") return Vector;
            if (token == "min") return Float;
            if (token == "min_extent") return IdentList(IdentifierType.Object);
            if (token == "minimum_reuse") return Float;
            if (token == "mm_per_unit") return Float;
            if (token == "mod") return Float;
            if (token == "mortar") return Float;
            if (token == "natural_spline")
            {
                if ((argNum % 2) == 0) return Float;
                return Vector;
            }
            if (token == "nearest_count") return Float;
            if (token == "noise_generator") return Float;
            if (token == "normal") return IdentList(IdentifierType.Normal);
            if (token == "normal_indices")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "normal_map") return IdentList(IdentifierType.NormalMap);
            if (token == "normal_vectors")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "number_of_waves") return Float;
            if (token == "object") return IdentList(IdentifierType.Object);
            if (token == "octaves") return Float;
            if (token == "offset") return Vector;
            if (token == "omega") return Float;



            if (token == "onion") return MapType(sText, ref pos, ref token);

            if (token == "orientation") return Vector;
            if (token == "ovus") return Float;
            if (token == "parametric") return IdentList(IdentifierType.Function);
            if (token == "pattern") return IdentList(IdentifierType.Pattern);
            if (token == "pavement") return MapType(sText, ref pos, ref token);
            if (token == "pgm") return IdentList(IdentifierType.String);
            if (token == "phase") return Float;
            if (token == "phong") return Float;
            if (token == "phong_size") return Float;


            if (token == "pigment") return IdentList(IdentifierType.Pigment);
            if (token == "pigment_map") return IdentList(IdentifierType.PigmentMap);
            if (token == "pigment_pattern") return IdentList(IdentifierType.Pigment);
            if (token == "planar") return MapType(sText, ref pos, ref token);
            if (token == "plane")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Float;
            }
            if (token == "png") return IdentList(IdentifierType.String);
            if (token == "point_at") return Vector;
            if (token == "poly") return Float;
            if (token == "polynomial") return Float;
            if (token == "poly_wave") return Float;
            if (token == "polygon")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "pot") return IdentList(IdentifierType.String);
            if (token == "pow") return Float;
            if (token == "ppm") return IdentList(IdentifierType.String);
            if (token == "precision") return Float;
            if (token == "precompute") return Float;

            if (token == "prod") return Float;
            if (token == "projected_through") return IdentList(IdentifierType.Object);
            if (token == "pwr") return Float;
            if (token == "quadratic_spline")
            {
                if (argNum >= 0 && argNum <= 2) return Float;
                return Vector;

            }
            if (token == "quadric") return Float;
            if (token == "quartic") return Float;

            if (token == "quick_color") return ColorOnly;
            if (token == "quick_colour") return ColorOnly;
            if (token == "quilted") return MapType(sText, ref pos, ref token);
            if (token == "radial") return MapType(sText, ref pos, ref token);
            if (token == "radians") return Float;

            if (token == "radius") return Float;
            if (token == "rainbow") return IdentList(IdentifierType.Rainbow);
            if (token == "rand") return Float;
            if (token == "range") return Float;
            if (token == "ratio") return Float;
            if (token == "reciprocal") return Float;
            if (token == "recursion_limit") return Float;
            if (token == "red") return Float;
            if (token == "reflection") return Vector;
            if (token == "reflection_exponent") return Float;
            if (token == "refraction") return Float;
            if (token == "render") return String;
            if (token == "repeat") return Vector;
            if (token == "rgb") return ColorOnly;
            if (token == "rgbf") return ColorOnly;
            if (token == "rgbft") return ColorOnly;
            if (token == "rgbt") return ColorOnly;
            if (token == "right") return Vector;
            if (token == "ripples") return MapType(sText, ref pos, ref token);
            if (token == "rotate") return Vector;
            if (token == "roughness") return Float;
            if (token == "samples") return Float;
            if (token == "save_file") return String;
            if (token == "scale") return Vector;
            if (token == "scattering") return Float;
            if (token == "seed") return Float;
            if (token == "select") return Float;
            if (token == "sin") return Float;
            if (token == "sinh") return Float;
            
            if (token == "size") return Float;
            if (token == "sky") return Vector;
            if (token == "sky_sphere") return IdentList(IdentifierType.SkySphere, IdentifierType.Pigment);
            if (token == "slice") return Vector;
            if (token == "slope")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Float;
                if (argNum == 2) return Float;
            }
            if (token == "slope_map") return IdentList(IdentifierType.SlopeMap);
            if (token == "smooth_triangle")
            {
                if (argNum < 6) return Vector;
            }
            if (token == "sor")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "spacing") return Float;
            if (token == "specular") return ColorOnly;
            if (token == "sphere")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Float;
            }
            if (token == "spherical") return MapType(sText, ref pos, ref token);
            if (token == "spiral1") return MapType(sText, ref pos, ref token);
            if (token == "spiral2") return MapType(sText, ref pos, ref token);
            if (token == "spline") return IdentList(IdentifierType.Spline);
            if (token == "split_union") return Bool;
            if (token == "spotted") return MapType(sText, ref pos, ref token);
            if (token == "sqrt") return Float;
            if (token == "square") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "srgb") return ColorOnly;
            if (token == "srgbf") return ColorOnly;
            if (token == "srgbt") return ColorOnly;
            if (token == "srgbft") return ColorOnly;
            if (token == "statistics") return String;
            if (token == "str")
            {
                if (argNum < 3) return Float;
            }
            if (token == "strcmp")
            {
                if (argNum < 2) return String;
            }
            if (token == "strength") return Float;
            if (token == "strlen") return String;
            if (token == "strlwr") return String;
            if (token == "strupr") return String;
            if (token == "substr")
            {
                if (argNum == 0) return String;
                if (argNum == 1) return Float;
                if (argNum == 2) return Float;
            }


            if (token == "sum") return Float;
            if (token == "superellipsoid") return Vector;
            if (token == "switch") return Float;
            if (token == "sys") return String;
            if (token == "tan") return Float;
            if (token == "tanh") return Float;
            if (token == "texture") return IdentList(IdentifierType.Texture);
            if (token == "texture_map") return IdentList(IdentifierType.TextureMap);
            if (token == "tga") return String;
            if (token == "thickness") return Float;
            if (token == "threshold") return Float;
            if (token == "tiff") return String;
            if (token == "tightness") return Float;
            if (token == "tile2") return IdentList(IdentifierType.Texture);
            if (token == "tiles") return IdentList(IdentifierType.Texture);
            if (token == "tiling") return Float;
            if (token == "tolerance") return Float;
            if (token == "toroidal") return MapType(sText, ref pos, ref token);
            if (token == "torus")
            {
                if (argNum < 2) return Float;
            }
            if (token == "trace")
            {
                if (argNum == 0) return IdentList(IdentifierType.Object);
                if (argNum == 1) return Vector;
                if (argNum == 2) return Vector;
                if (argNum == 3) return IdentList(IdentifierType.Vector);

            }
            if (token == "transform") return IdentList(IdentifierType.Transform);
            if (token == "translate") return Vector;
            if (token == "translucency") return ColorOnly;
            if (token == "transmit") return Float;
            if (token == "triangle")
            {
                if (argNum == 0) return Vector;
                if (argNum == 1) return Vector;
                if (argNum == 2) return Vector;
            }

            if (token == "triangular")
            {
                if (argNum < 6) return ColorOrTexture(sText, ref pos, ref token);
            }

            if (token == "ttf") return String;
            if (token == "turb_depth") return Float;
            if (token == "turbulence") return Float;
            if (token == "type") return Float;
            if (token == "u_steps") return Float;
            if (token == "union") return IdentList(IdentifierType.Object);
            if (token == "up") return Vector;
            if (token == "uv_indices")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "uv_vectors") return Vector;

            if (token == "v_steps") return Float;
            if (token == "val") return String;
            if (token == "variance") return Float;
            if (token == "vaxis_rotate") return Vector;
            if (token == "vcross") return Vector;
            if (token == "vdot") return Vector;
            if (token == "version") return Float;
            if (token == "vertex_vectors")
            {
                if (argNum == 0) return Float;
                return Vector;
            }
            if (token == "vlength") return Vector;
            if (token == "vnormalize") return Vector;
            if (token == "vrotate") return Vector;
            if (token == "vstr")
            {
                if (argNum == 0) return Float;
                if (argNum == 1) return Vector;
                if (argNum == 2) return String;
                if (argNum == 3) return Float;
                if (argNum == 4) return Float;

            }
            if (token == "vturbulence")
            {
                if (argNum == 0) return Float;
                if (argNum == 1) return Float;
                if (argNum == 2) return Float;
                if (argNum == 3) return Vector;
            }
            if (token == "warning") return String;
            if (token == "water_level") return Float;
            if (token == "waves") return MapType(sText, ref pos, ref token);
            if (token == "while") return Bool;
            if (token == "wood") return MapType(sText, ref pos, ref token);
            if (token == "wrinkles") return MapType(sText, ref pos, ref token);
            if (token == "write") return IdentList(IdentifierType.FileIdentifier);

            // Otherwise, don't suggest any identifiers.
            return new IdentifierType[] { };
        }

        public IdentifierType[] ValidIdentifierTypes(int line, int index)
        {
            string sText;

            int lastLineLen;
            int last;

            buffer.GetLastLineIndex(out last, out lastLineLen);
            buffer.GetLengthOfLine(last, out lastLineLen);
            buffer.GetLineText(0, 0, last, lastLineLen, out sText);

            int pos;

            buffer.GetPositionOfLineIndex(line, index, out pos);

            // Strip out all of the comments
            sText = StripComments(sText, ref pos);

            bool found = false;
            int argNum = 0;
            string token ="";
            // Walk back through the tokens, looking for a keyword. 
            // Argument count is tracked so keywords that have multiple
            // arguments expecting different types can be handles appropriately. 
            while (!found)
            {
                token = FindPrevToken(sText, ref pos);

                if (PovCodeCompletion.keywords.Contains(token))
                {
                    found = true;
                }
                else if (token == ",")
                {
                    argNum++;
                }

            }

            
            if (token == "agate")
            {
                if (IsInNormalBlock(sText, ref pos, ref token))
                {
                    return IdentList(IdentifierType.Float, IdentifierType.Function);
                }
                else
                {
                    return MapType(sText, ref pos, ref token);
                }

            }

            if (token == "average") return MapType(sText, ref pos, ref token);

            if (token == "aa_level") return FloatOrFunction;
            if (token == "aa_threshold") return FloatOrFunction;
            if (token == "abs") return FloatOrFunction;
            if (token == "absorption") return Color;
            if (token == "accuracy") return FloatOrFunction;
            if (token == "acos") return FloatOrFunction;
            if (token == "acosh") return FloatOrFunction;
            if (token == "adc_bailout") return FloatOrFunction;
            if (token == "agate_turb") return FloatOrFunction;
            if (token == "albedo") return FloatOrFunction;
            if (token == "all") return FloatOrFunction;
            if (token == "alpha") return FloatOrFunction;
            if (token == "altitude") return FloatOrFunction;
            if (token == "always_sample") return BoolOrFunction;
            if (token == "angle") return FloatOrFunction;
            if (token == "aoi") return FloatOrFunction;
            if (token == "aperture") return FloatOrFunction;
            if (token == "asin") return FloatOrFunction;
            if (token == "asinh") return FloatOrFunction;
            if (token == "assumed_gamma") return FloatOrFunction;
            if (token == "atan") return FloatOrFunction;
            if (token == "atan2") return FloatOrFunction;
            if (token == "atand") return FloatOrFunction;
            if (token == "atanh") return FloatOrFunction;

            if (token == "ambient") return Color;
            if (token == "ambient_light") return Color;
            if (token == "arc_angle") return FloatOrFunction;
            if (token == "background") return Color;

            if (token == "area_illumination") return BoolOrFunction;

            if (token == "asc") return String;

            if (token == "black_hole") return VectorOrFunction;

            if (token == "bokeh") return new IdentifierType[] { IdentifierType.Pigment };


            if (token == "b_spline") return FloatOrFunction;


            if (token == "autostop") return FloatOrFunction;
            if (token == "bezier_spline")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "bitwise_and") return FloatOrFunction;
            if (token == "bitwise_or") return FloatOrFunction;
            if (token == "bitwise_xor") return FloatOrFunction;
            if (token == "blue") return FloatOrFunction;
            if (token == "blur_samples") return FloatOrFunction;

            if (token == "bounded_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "box") return VectorOrFunction;
            if (token == "boxed") return MapType(sText, ref pos, ref token);
            if (token == "bozo") return MapType(sText, ref pos, ref token);
            if (token == "brick") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "brick_size") return FloatOrFunction;
            if (token == "brightness") return FloatOrFunction;
            if (token == "brilliance") return FloatOrFunction;

            if (token == "bump_size") return FloatOrFunction;
            if (token == "bumps") return MapType(sText, ref pos, ref token);
            if (token == "camera") return new IdentifierType[] { IdentifierType.Camera };
            if (token == "case") return FloatOrFunction;
            if (token == "caustics") return FloatOrFunction;
            if (token == "ceil") return FloatOrFunction;
            if (token == "cells") return MapType(sText, ref pos, ref token);

            if (token == "checker") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "chr") return FloatOrFunction;
            if (token == "clipped_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "collect") return BoolOrFunction;
            if (token == "color") return Color;
            if (token == "color_map") return new IdentifierType[] { IdentifierType.ColorMap };
            if (token == "colour") return Color;
            if (token == "colour_map") return new IdentifierType[] { IdentifierType.ColorMap };
            if (token == "component")
            {
                if (argNum == 2) return VectorOrFunction;
                return FloatOrFunction;
            }
            if (token == "composite") return new IdentifierType[] { IdentifierType.Object };
            if (token == "concat") return new IdentifierType[] { IdentifierType.String };
            if (token == "cone")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return FloatOrFunction;
                if (argNum == 2) return VectorOrFunction;
                if (argNum == 3) return FloatOrFunction;
            }
            if (token == "confidence") return FloatOrFunction;
            if (token == "conic_sweep")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "contained_by") return new IdentifierType[] { IdentifierType.Object };
            if (token == "control0") return FloatOrFunction;
            if (token == "control1") return FloatOrFunction;
            if (token == "coords") return VectorOrFunction;
            if (token == "cos") return FloatOrFunction;
            if (token == "cosh") return FloatOrFunction;
            if (token == "count") return FloatOrFunction;
            if (token == "crackle") return MapType(sText, ref pos, ref token);
            if (token == "crand") return FloatOrFunction;
            if (token == "cubic") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "cubic_spline")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "cubic_wave") return MapType(sText, ref pos, ref token);
            if (token == "cylinder")
            {
                if (argNum == 0 || argNum == 1) return VectorOrFunction;
                if (argNum == 2) return FloatOrFunction;
            }
            if (token == "cylindrical") return MapType(sText, ref pos, ref token);
            if (token == "datetime")
            {
                if (argNum == 0) return FloatOrFunction;
                if (argNum == 1) return new IdentifierType[] { IdentifierType.String };
            }
            if (token == "debug") return new IdentifierType[] { IdentifierType.String };

            if (token == "default") return IdentList(IdentifierType.Texture, IdentifierType.Pigment, IdentifierType.Finish, IdentifierType.Normal);
            if (token == "defined") return IdentList(IdentifierType.Unknown);
            if (token == "degrees") return FloatOrFunction;
            if (token == "density") return IdentList(IdentifierType.DensityMap);
            if (token == "density_file") return IdentList(IdentifierType.String);
            if (token == "density_map") return IdentList(IdentifierType.DensityMap);
            if (token == "dents") return MapType(sText, ref pos, ref token);
            if (token == "df3") return IdentList(IdentifierType.String);
            if (token == "difference") IdentList(IdentifierType.Object);
            if (token == "diffuse") return VectorOrFunction;
            if (token == "dimension_size")
            {
                if (argNum == 0) return IdentList(IdentifierType.Array);
                return IdentList(IdentifierType.Float);
            }
            if (token == "dimensions") return IdentList(IdentifierType.Array);
            if (token == "direction") return VectorOrFunction;
            if (token == "disc")
            {
                if (argNum == 0 || argNum == 1) return VectorOrFunction;
                if (argNum == 2) return FloatOrFunction;
                if (argNum == 3) return FloatOrFunction;
            }
            if (token == "dispersion") return FloatOrFunction;
            if (token == "dispersion_samples") return FloatOrFunction;
            if (token == "dist_exp") return FloatOrFunction;
            if (token == "distance") return FloatOrFunction;
            if (token == "div") return FloatOrFunction;
            if (token == "eccentricity") return FloatOrFunction;
            if (token == "emission") return Color;
            if (token == "error") return IdentList(IdentifierType.String);
            if (token == "error_bound") return FloatOrFunction;
            if (token == "evaluate") return FloatOrFunction;
            if (token == "exp") return FloatOrFunction;
            if (token == "expand_thresholds") return FloatOrFunction;
            if (token == "exponent") return FloatOrFunction;
            if (token == "extinction") return FloatOrFunction;
            if (token == "face_indices")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "facets") return MapType(sText, ref pos, ref token);
            if (token == "fade_color") return Color;
            if (token == "fade_colour") return Color;
            if (token == "fade_distance") return FloatOrFunction;
            if (token == "fade_power") return FloatOrFunction;
            if (token == "falloff") return FloatOrFunction;
            if (token == "falloff_angle") return FloatOrFunction;
            if (token == "fclose") return IdentList(IdentifierType.FileIdentifier);
            if (token == "file_exists") return IdentList(IdentifierType.String);
            if (token == "filter") return FloatOrFunction;
            if (token == "finish") return IdentList(IdentifierType.Finish);
            if (token == "flatness") return FloatOrFunction;
            if (token == "flip") return VectorOrFunction;
            if (token == "floor") return FloatOrFunction;
            if (token == "focal_point") return VectorOrFunction;
            if (token == "fog") return IdentList(IdentifierType.Fog);
            if (token == "fog_alt") return FloatOrFunction;
            if (token == "fog_offset") return FloatOrFunction;
            if (token == "fog_type") return FloatOrFunction;
            if (token == "for") return FloatOrFunction;
            if (token == "form") return VectorOrFunction;
            if (token == "frequency") return FloatOrFunction;
            if (token == "fresnel") return BoolOrFunction;
            if (token == "function") return IdentList(IdentifierType.Unknown);
            if (token == "gamma") return FloatOrFunction;
            if (token == "gather") return FloatOrFunction;
            if (token == "gif") return IdentList(IdentifierType.String);
            if (token == "global_lights") return BoolOrFunction;
            if (token == "gradient") return MapType(sText, ref pos, ref token);
            if (token == "granite") return MapType(sText, ref pos, ref token);
            if (token == "gray_threshold") return FloatOrFunction;
            if (token == "green") return FloatOrFunction;
            if (token == "hexagon") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "hierarchy") return BoolOrFunction;
            if (token == "hollow") return BoolOrFunction;
            if (token == "if") return BoolOrFunction;
            if (token == "ifdef") return IdentList(IdentifierType.Unknown);
            if (token == "iff") return IdentList(IdentifierType.String);
            if (token == "ifndef") return IdentList(IdentifierType.Unknown);
            if (token == "importance") return FloatOrFunction;
            if (token == "include") return IdentList(IdentifierType.String);
            if (token == "inside")
            {
                if (argNum == 0) return IdentList(IdentifierType.Object);
                return VectorOrFunction;
            }
            if (token == "inside_vector") return VectorOrFunction;
            if (token == "int") return FloatOrFunction;
            if (token == "interior") return IdentList(IdentifierType.Interior);
            if (token == "interior_texture") return IdentList(IdentifierType.Texture, IdentifierType.Pigment, IdentifierType.Normal, IdentifierType.Finish);
            if (token == "internal") return FloatOrFunction;
            if (token == "interpolate") return FloatOrFunction;
            if (token == "intersection") return IdentList(IdentifierType.Object);
            if (token == "intervals") return FloatOrFunction;
            if (token == "ior") return MapType(sText, ref pos, ref token);
            if (token == "irid") return FloatOrFunction;
            if (token == "irid_wavelength") return FloatOrFunction;
            if (token == "isosurface") return IdentList(IdentifierType.Function);
            if (token == "jitter") return FloatOrFunction;
            if (token == "jpeg") return IdentList(IdentifierType.String);
            if (token == "julia")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return FloatOrFunction;
                if (argNum == 2) return FloatOrFunction;
            }
            if (token == "julia_fractal") return VectorOrFunction;
            if (token == "lambda") return FloatOrFunction;
            if (token == "leopard") return MapType(sText, ref pos, ref token);
            if (token == "light_group") return IdentList(IdentifierType.Object, IdentifierType.LightSource);
            if (token == "light_source") return IdentList(IdentifierType.LightSource, IdentifierType.Vector, IdentifierType.Float, IdentifierType.Function);
            if (token == "linear_spline")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "linear_sweep")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "ln") return FloatOrFunction;
            if (token == "load_file") return IdentList(IdentifierType.String);
            if (token == "location") return VectorOrFunction;
            if (token == "log") return FloatOrFunction;
            if (token == "look_at") return VectorOrFunction;
            if (token == "looks_like") return IdentList(IdentifierType.Object);
            if (token == "low_error_factor") return FloatOrFunction;
            if (token == "magnet")
            {
                if (argNum == 0) return FloatOrFunction;
                else return new IdentifierType[] { };
            }
            if (token == "major_radius") return FloatOrFunction;
            if (token == "mandel") return FloatOrFunction;
            if (token == "map_type") return FloatOrFunction;
            if (token == "marble") return MapType(sText, ref pos, ref token);
            if (token == "material") return IdentList(IdentifierType.Material);
            if (token == "max") return FloatOrFunction;
            if (token == "maximum_reuse") return FloatOrFunction;
            if (token == "max_extent") return IdentList(IdentifierType.Object);
            if (token == "max_gradient") return FloatOrFunction;
            if (token == "max_intersections") return FloatOrFunction;
            if (token == "max_iteration") return FloatOrFunction;
            if (token == "max_sample") return FloatOrFunction;
            if (token == "max_trace") return FloatOrFunction;
            if (token == "max_trace_level") return FloatOrFunction;
            if (token == "media") return IdentList(IdentifierType.Media);
            if (token == "media_attenuation") return BoolOrFunction;
            if (token == "media_interaction") return BoolOrFunction;
            if (token == "merge") return IdentList(IdentifierType.Object);
            if (token == "method") return FloatOrFunction;
            if (token == "metric") return VectorOrFunction;
            if (token == "min") return FloatOrFunction;
            if (token == "min_extent") return IdentList(IdentifierType.Object);
            if (token == "minimum_reuse") return FloatOrFunction;
            if (token == "mm_per_unit") return FloatOrFunction;
            if (token == "mod") return FloatOrFunction;
            if (token == "mortar") return FloatOrFunction;
            if (token == "natural_spline")
            {
                if ((argNum % 2) == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "nearest_count") return FloatOrFunction;
            if (token == "noise_generator") return FloatOrFunction;
            if (token == "normal") return IdentList(IdentifierType.Normal);
            if (token == "normal_indices")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "normal_map") return IdentList(IdentifierType.NormalMap);
            if (token == "normal_vectors")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "number_of_waves") return FloatOrFunction;
            if (token == "object") return IdentList(IdentifierType.Object);
            if (token == "octaves") return FloatOrFunction;
            if (token == "offset") return VectorOrFunction;
            if (token == "omega") return FloatOrFunction;



            if (token == "onion") return MapType(sText, ref pos, ref token);

            if (token == "orientation") return VectorOrFunction;
            if (token == "ovus") return FloatOrFunction;
            if (token == "parametric") return IdentList(IdentifierType.Function);
            if (token == "pattern") return IdentList(IdentifierType.Pattern);
            if (token == "pavement") return MapType(sText, ref pos, ref token);
            if (token == "pgm") return IdentList(IdentifierType.String);
            if (token == "phase") return FloatOrFunction;
            if (token == "phong") return FloatOrFunction;
            if (token == "phong_size") return FloatOrFunction;


            if (token == "pigment") return IdentList(IdentifierType.Pigment);
            if (token == "pigment_map") return IdentList(IdentifierType.PigmentMap);
            if (token == "pigment_pattern") return IdentList(IdentifierType.Pigment);
            if (token == "planar") return MapType(sText, ref pos, ref token);
            if (token == "plane")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return FloatOrFunction;
            }
            if (token == "png") return IdentList(IdentifierType.String);
            if (token == "point_at") return VectorOrFunction;
            if (token == "poly") return FloatOrFunction;
            if (token == "polynomial") return FloatOrFunction;
            if (token == "poly_wave") return FloatOrFunction;
            if (token == "polygon")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "pot") return IdentList(IdentifierType.String);
            if (token == "pow") return FloatOrFunction;
            if (token == "ppm") return IdentList(IdentifierType.String);
            if (token == "precision") return FloatOrFunction;
            if (token == "precompute") return FloatOrFunction;

            if (token == "prod") return FloatOrFunction;
            if (token == "projected_through") return IdentList(IdentifierType.Object);
            if (token == "pwr") return FloatOrFunction;
            if (token == "quadratic_spline")
            {
                if (argNum >= 0 && argNum <= 2) return FloatOrFunction;
                return VectorOrFunction;

            }
            if (token == "quadric") return FloatOrFunction;
            if (token == "quartic") return FloatOrFunction;

            if (token == "quick_color") return Color;
            if (token == "quick_colour") return Color;
            if (token == "quilted") return MapType(sText, ref pos, ref token);
            if (token == "radial") return MapType(sText, ref pos, ref token);
            if (token == "radians") return FloatOrFunction;

            if (token == "radius") return FloatOrFunction;
            if (token == "rainbow") return IdentList(IdentifierType.Rainbow);
            if (token == "rand") return FloatOrFunction;
            if (token == "range") return FloatOrFunction;
            if (token == "ratio") return FloatOrFunction;
            if (token == "reciprocal") return FloatOrFunction;
            if (token == "recursion_limit") return FloatOrFunction;
            if (token == "red") return FloatOrFunction;
            if (token == "reflection") return VectorOrFunction;
            if (token == "reflection_exponent") return FloatOrFunction;
            if (token == "refraction") return FloatOrFunction;
            if (token == "render") return StringOrFunction;
            if (token == "repeat") return VectorOrFunction;
            if (token == "rgb") return Color;
            if (token == "rgbf") return Color;
            if (token == "rgbft") return Color;
            if (token == "rgbt") return Color;
            if (token == "right") return VectorOrFunction;
            if (token == "ripples") return MapType(sText, ref pos, ref token);
            if (token == "rotate") return VectorOrFunction;
            if (token == "roughness") return FloatOrFunction;
            if (token == "samples") return FloatOrFunction;
            if (token == "save_file") return StringOrFunction;
            if (token == "scale") return VectorOrFunction;
            if (token == "scattering") return FloatOrFunction;
            if (token == "seed") return FloatOrFunction;
            if (token == "select") return FloatOrFunction;
            if (token == "sin") return FloatOrFunction;
            if (token == "sinh") return FloatOrFunction;
            if (token == "sint8") return IdentList(IdentifierType.Unknown);
            if (token == "sint16be") return IdentList(IdentifierType.Unknown);
            if (token == "sint16le") return IdentList(IdentifierType.Unknown);
            if (token == "sint32be") return IdentList(IdentifierType.Unknown);
            if (token == "sint32le") return IdentList(IdentifierType.Unknown);
            if (token == "size") return FloatOrFunction;
            if (token == "sky") return VectorOrFunction;
            if (token == "sky_sphere") return IdentList(IdentifierType.SkySphere, IdentifierType.Pigment);
            if (token == "slice") return VectorOrFunction;
            if (token == "slope")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return FloatOrFunction;
                if (argNum == 2) return FloatOrFunction;
            }
            if (token == "slope_map") return IdentList(IdentifierType.SlopeMap);
            if (token == "smooth_triangle")
            {
                if (argNum < 6) return VectorOrFunction;
            }
            if (token == "sor")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "spacing") return FloatOrFunction;
            if (token == "specular") return Color;
            if (token == "sphere")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return FloatOrFunction;
            }
            if (token == "spherical") return MapType(sText, ref pos, ref token);
            if (token == "spiral1") return MapType(sText, ref pos, ref token);
            if (token == "spiral2") return MapType(sText, ref pos, ref token);
            if (token == "spline") return IdentList(IdentifierType.Spline);
            if (token == "split_union") return BoolOrFunction;
            if (token == "spotted") return MapType(sText, ref pos, ref token);
            if (token == "sqrt") return FloatOrFunction;
            if (token == "square") return ColorOrTexture(sText, ref pos, ref token);
            if (token == "srgb") return Color;
            if (token == "srgbf") return Color;
            if (token == "srgbt") return Color;
            if (token == "srgbft") return Color;
            if (token == "statistics") return StringOrFunction;
            if (token == "str")
            {
                if (argNum < 3) return FloatOrFunction;
            }
            if (token == "strcmp")
            {
                if (argNum < 2) return StringOrFunction;
            }
            if (token == "strength") return FloatOrFunction;
            if (token == "strlen") return StringOrFunction;
            if (token == "strlwr") return StringOrFunction;
            if (token == "strupr") return StringOrFunction;
            if (token == "substr")
            {
                if (argNum == 0) return StringOrFunction;
                if (argNum == 1) return FloatOrFunction;
                if (argNum == 2) return FloatOrFunction;
            }


            if (token == "sum") return FloatOrFunction;
            if (token == "superellipsoid") return VectorOrFunction;
            if (token == "switch") return FloatOrFunction;
            if (token == "sys") return StringOrFunction;
            if (token == "tan") return FloatOrFunction;
            if (token == "tanh") return FloatOrFunction;
            if (token == "texture") return IdentList(IdentifierType.Texture);
            if (token == "texture_map") return IdentList(IdentifierType.TextureMap);
            if (token == "tga") return StringOrFunction;
            if (token == "thickness") return FloatOrFunction;
            if (token == "threshold") return FloatOrFunction;
            if (token == "tiff") return StringOrFunction;
            if (token == "tightness") return FloatOrFunction;
            if (token == "tile2") return IdentList(IdentifierType.Texture);
            if (token == "tiles") return IdentList(IdentifierType.Texture);
            if (token == "tiling") return FloatOrFunction;
            if (token == "tolerance") return FloatOrFunction;
            if (token == "toroidal") return MapType(sText, ref pos, ref token);
            if (token == "torus")
            {
                if (argNum < 2) return FloatOrFunction;
            }
            if (token == "trace")
            {
                if (argNum == 0) return IdentList(IdentifierType.Object);
                if (argNum == 1) return VectorOrFunction;
                if (argNum == 2) return VectorOrFunction;
                if (argNum == 3) return IdentList(IdentifierType.Vector);

            }
            if (token == "transform") return IdentList(IdentifierType.Transform);
            if (token == "translate") return VectorOrFunction;
            if (token == "translucency") return Color;
            if (token == "transmit") return FloatOrFunction;
            if (token == "triangle")
            {
                if (argNum == 0) return VectorOrFunction;
                if (argNum == 1) return VectorOrFunction;
                if (argNum == 2) return VectorOrFunction;
            }

            if (token == "triangular")
            {
                if (argNum < 6) return ColorOrTexture(sText, ref pos, ref token);
            }

            if (token == "ttf") return StringOrFunction;
            if (token == "turb_depth") return FloatOrFunction;
            if (token == "turbulence") return FloatOrFunction;
            if (token == "type") return FloatOrFunction;
            if (token == "uint8") return Any;
            if (token == "uint16be") return Any;
            if (token == "uint16le") return Any;
            if (token == "u_steps") return FloatOrFunction;
            if (token == "undef") return Any;
            if (token == "union") return IdentList(IdentifierType.Object);
            if (token == "up") return VectorOrFunction;
            if (token == "uv_indices")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "uv_vectors") return VectorOrFunction;

            if (token == "v_steps") return FloatOrFunction;
            if (token == "val") return StringOrFunction;
            if (token == "variance") return FloatOrFunction;
            if (token == "vaxis_rotate") return VectorOrFunction;
            if (token == "vcross") return VectorOrFunction;
            if (token == "vdot") return VectorOrFunction;
            if (token == "version") return FloatOrFunction;
            if (token == "vertex_vectors")
            {
                if (argNum == 0) return FloatOrFunction;
                return VectorOrFunction;
            }
            if (token == "vlength") return VectorOrFunction;
            if (token == "vnormalize") return VectorOrFunction;
            if (token == "vrotate") return VectorOrFunction;
            if (token == "vstr")
            {
                if (argNum == 0) return FloatOrFunction;
                if (argNum == 1) return VectorOrFunction;
                if (argNum == 2) return StringOrFunction;
                if (argNum == 3) return FloatOrFunction;
                if (argNum == 4) return FloatOrFunction;

            }
            if (token == "vturbulence")
            {
                if (argNum == 0) return FloatOrFunction;
                if (argNum == 1) return FloatOrFunction;
                if (argNum == 2) return FloatOrFunction;
                if (argNum == 3) return VectorOrFunction;
            }
            if (token == "warning") return StringOrFunction;
            if (token == "water_level") return FloatOrFunction;
            if (token == "waves") return MapType(sText, ref pos, ref token);
            if (token == "while") return BoolOrFunction;
            if (token == "wood") return MapType(sText, ref pos, ref token);
            if (token == "wrinkles") return MapType(sText, ref pos, ref token);
            if (token == "write") return IdentList(IdentifierType.FileIdentifier);

            // Otherwise, don't suggest any identifiers.
            return new IdentifierType[] { };
        }

        private IdentifierType[] MapType(string sText, ref int pos, ref string token)
        {
            if (IsInNormalBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.NormalMap };
            if (IsInPigmentBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.PigmentMap, IdentifierType.ColorMap };
            if (IsInTextureBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.TextureMap };
            return new IdentifierType[] { };
        }

        private IdentifierType[] ColorOrTexture(string sText, ref int pos, ref string token)
        {
            if (IsInNormalBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.Normal };
            if (IsInPigmentBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.Pigment, IdentifierType.Color };
            if (IsInTextureBlock(sText, ref pos, ref token)) return new IdentifierType[] { IdentifierType.Texture };
            return new IdentifierType[] { };
        }

        private bool IsInNormalBlock(string sText, ref int pos, ref string token)
        {
            while (token != "{") token = FindPrevToken(sText, ref pos); // find the opening of the block
            token = FindPrevToken(sText, ref pos);                     // This will be the block it lives in

            if (token == "normal") return true;
            return false;
        }

        private bool IsInPigmentBlock(string sText, ref int pos, ref string token)
        {
            while (token != "{") token = FindPrevToken(sText, ref pos); // find the opening of the block
            token = FindPrevToken(sText, ref pos);                     // This will be the block it lives in

            if (token == "pigment") return true;
            return false;
        }

        private bool IsInFinishBlock(string sText, ref int pos, ref string token)
        {
            while (token != "{") token = FindPrevToken(sText, ref pos); // find the opening of the block
            token = FindPrevToken(sText, ref pos);                     // This will be the block it lives in

            if (token == "finish") return true;
            return false;
        }
        private bool IsInTextureBlock(string sText, ref int pos, ref string token)
        {
            while (token != "{") token = FindPrevToken(sText, ref pos); // find the opening of the block
            token = FindPrevToken(sText, ref pos);                     // This will be the block it lives in

            if (token == "texture") return true;
            return false;
        }

        enum StripState
        {
            Normal,
            InLineComment,
            InBlockComment,
            InCommentPrefix,
            InCommentEndPrefix
        }



        private static readonly IdentifierType[] NoIdentifiers = new IdentifierType[] { };

        private string StripComments(string sText, ref int pos)
        {
            StringBuilder ret = new StringBuilder();
            int i = 0;



            StripState state = StripState.Normal;

            while (i < sText.Length)
            {
                switch (state)
                {
                    case StripState.Normal:
                        if (sText[i] == '/')
                        {
                            state = StripState.InCommentPrefix;

                        }

                        ret.Append(sText[i]);
                        break;
                    case StripState.InLineComment:
                        if (sText[i] == '\n' || sText[i] == '\r')
                        {
                            state = StripState.Normal;
                            ret.Append(sText[i]);
                        }
                        else
                        {
                            if (ret.Length < pos) pos--;
                        }
                        break;
                    case StripState.InBlockComment:
                        if (sText[i] == '*')
                        {
                            state = StripState.InCommentEndPrefix;
                            if (ret.Length < pos) pos--;
                        }
                        break;
                    case StripState.InCommentEndPrefix:
                        if (sText[i] == '/')
                        {
                            state = StripState.Normal;
                            if (ret.Length < pos) pos--;
                        }
                        break;
                    case StripState.InCommentPrefix:
                        if (sText[i] == '/')
                        {
                            ret.Remove(ret.Length - 1, 1);
                            state = StripState.InLineComment;
                            if (ret.Length < pos) pos -= 2;
                        }
                        else if (sText[i] == '*')
                        {
                            ret.Remove(ret.Length - 1, 1);
                            state = StripState.InBlockComment;
                            if (ret.Length < pos) pos -= 2;
                        }
                        else
                        {
                            state = StripState.Normal;
                        }
                        break;
                    default:
                        break;
                }
                i++;
            }
            return ret.ToString();
        }



        private static readonly IdentifierType[] Color = new IdentifierType[] { IdentifierType.Color, IdentifierType.Vector, IdentifierType.Float };
        private static readonly IdentifierType[] ColorOnly = new IdentifierType[] { IdentifierType.Color };
    }
}
